package com.jneagle.xlstool.bzwgjmx.impl.handler;

import com.dwarfeng.subgrade.stack.bean.key.KeyFetcher;
import com.dwarfeng.subgrade.stack.bean.key.LongIdKey;
import com.dwarfeng.subgrade.stack.exception.HandlerException;
import com.jneagle.xlstool.bzwgjmx.impl.exception.ParserUnableDetermineException;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawErrorInfo;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPart;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPartData;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.StructuredData;
import com.jneagle.xlstool.bzwgjmx.stack.handler.RawDataLoadHandler;
import com.jneagle.xlstool.bzwgjmx.stack.service.*;
import com.jneagle.xlstool.bzwgjmx.stack.structure.ProgressStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class RawDataLoadHandlerImpl extends AbstractProgressHandler implements RawDataLoadHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RawDataLoadHandlerImpl.class);

    private static final int CONFLICT_APPEND_POLICY_DO_NOT_APPEND = 0;
    private static final int CONFLICT_APPEND_POLICY_ALWAYS_APPEND = 1;

    private final RawPartMaintainService rawPartMaintainService;
    private final RawPartDataMaintainService rawPartDataMaintainService;
    private final RawErrorInfoMaintainService rawErrorInfoMaintainService;
    private final ExportDataMaintainService exportDataMaintainService;
    private final ExportErrorInfoMaintainService exportErrorInfoMaintainService;
    private final StructuredDataMaintainService structuredDataMaintainService;

    private final RawDataRowParserFactory rawDataRowParserFactory;

    private final KeyFetcher<LongIdKey> keyFetcher;

    @Value("${raw_data.policy.part_no.regex}")
    private String partNoRegex;
    @Value("${raw_data.policy.part_no.replacement}")
    private String partNoReplacement;

    @Value("${raw_data.policy.conflict_append}")
    private int conflictAppendPolicy;

    @Value("${raw_data.file_encoding}")
    private String rawFileEncoding;

    public RawDataLoadHandlerImpl(
            RawPartMaintainService rawPartMaintainService,
            RawPartDataMaintainService rawPartDataMaintainService,
            RawErrorInfoMaintainService rawErrorInfoMaintainService,
            ExportDataMaintainService exportDataMaintainService,
            ExportErrorInfoMaintainService exportErrorInfoMaintainService,
            StructuredDataMaintainService structuredDataMaintainService,
            RawDataRowParserFactory rawDataRowParserFactory,
            KeyFetcher<LongIdKey> keyFetcher
    ) {
        this.rawPartMaintainService = rawPartMaintainService;
        this.rawPartDataMaintainService = rawPartDataMaintainService;
        this.rawErrorInfoMaintainService = rawErrorInfoMaintainService;
        this.exportDataMaintainService = exportDataMaintainService;
        this.exportErrorInfoMaintainService = exportErrorInfoMaintainService;
        this.structuredDataMaintainService = structuredDataMaintainService;
        this.rawDataRowParserFactory = rawDataRowParserFactory;
        this.keyFetcher = keyFetcher;
    }

    @Override
    public void load(List<File> files) throws HandlerException {
        try {
            // 广播进度变更事件。
            fireProgressChanged(ProgressStatus.UNCERTAIN);

            // 清空相关实体的维护服务。
            rawPartMaintainService.clear();
            rawPartDataMaintainService.clear();
            rawErrorInfoMaintainService.clear();
            exportDataMaintainService.clear();
            exportErrorInfoMaintainService.clear();

            // 获取结构化数据的部件空间。
            Set<String> partNoSpace = structuredDataMaintainService.lookupAsList().stream()
                    .map(StructuredData::getPartNo).collect(Collectors.toSet());

            // 定义数据结果列表。
            List<RawPart> rawPartList = new ArrayList<>();
            List<RawPartData> rawPartDataList = new ArrayList<>();
            List<RawErrorInfo> rawErrorInfoList = new ArrayList<>();

            // 遍历文件，批量读取数据。
            for (int i = 0; i < files.size(); i++) {
                // 获取到索引对应的文件。
                File file = files.get(i);
                // 解析单个文件。
                parseSingleFile(file, partNoSpace, rawPartList, rawPartDataList, rawErrorInfoList);
                // 广播进度变更时间。
                fireProgressChanged(i + 1, files.size());
            }

            // 将结果添加到相关实体的维护服务。
            rawPartMaintainService.batchInsert(rawPartList);
            rawPartDataMaintainService.batchInsert(rawPartDataList);
            rawErrorInfoMaintainService.batchInsert(rawErrorInfoList);
        } catch (HandlerException e) {
            throw e;
        } catch (Exception e) {
            throw new HandlerException(e);
        } finally {
            // 广播进度变更事件。
            fireProgressChanged(ProgressStatus.IDLE);
        }
    }

    // 为了代码的可阅读性，保留其中的 switch-case 结构。
    @SuppressWarnings("DuplicateBranchesInSwitch")
    private void parseSingleFile(
            File file, Set<String> partNoSpace, List<RawPart> rawPartList, List<RawPartData> rawPartDataList,
            List<RawErrorInfo> rawErrorInfoList
    ) throws Exception {
        // 生成 RawPart 的主键。
        LongIdKey rawPartKey = keyFetcher.fetchKey();
        // 获取原始文件名称。
        String originFileName = file.getName();
        // 替换原始名称，获得部件号。
        String partNo = originFileName.replaceAll(partNoRegex, partNoReplacement);
        // 根据原始数据解析器工厂获得文件对应的解析器。
        RawDataRowParser parser;
        try {
            parser = rawDataRowParserFactory.getParser(file);
        } catch (ParserUnableDetermineException e) {
            LOGGER.warn("文件 " + originFileName + " 无法找到对应的解析器，请严格按照规定的格式对文件进行命名", e);
            rawErrorInfoList.add(new RawErrorInfo(
                    null, partNo, originFileName, null, null, "ui.label.037"
            ));
            return;
        }
        // 获取解析器的名称。
        String parserName = parser.getName();
        // 冲突分析与追加标记处理。
        boolean conflictFlag = partNoSpace.contains(partNo);
        boolean appendFlag;
        if (conflictFlag) {
            switch (conflictAppendPolicy) {
                case CONFLICT_APPEND_POLICY_DO_NOT_APPEND:
                    appendFlag = false;
                    break;
                case CONFLICT_APPEND_POLICY_ALWAYS_APPEND:
                    appendFlag = true;
                    break;
                default:
                    appendFlag = false;
                    break;
            }
        } else {
            appendFlag = true;
        }

        // 向 rawPartList 添加 RawPart。
        rawPartList.add(new RawPart(
                rawPartKey, partNo, originFileName, parserName, conflictFlag, appendFlag
        ));

        // 以 Scanner 的形式加载文件的输入流，注意使用指定的字符集。
        try (
                FileInputStream in = new FileInputStream(file);
                Scanner scanner = new Scanner(in, rawFileEncoding)
        ) {
            int rowIndex = 1;
            while (scanner.hasNextLine()) {
                String rowContent = scanner.nextLine();
                try {
                    rawPartDataList.add(parser.parseRow(rawPartKey, rowIndex, rowContent));
                } catch (Exception e) {
                    LOGGER.warn("解析文件 " + originFileName + " 第 " + rowIndex + " 行时出现异常，异常信息如下: ", e);
                    rawErrorInfoList.add(new RawErrorInfo(
                            null, partNo, originFileName, parserName, rowIndex, "ui.label.038"
                    ));
                }
                rowIndex++;
            }
        }
    }
}
